package preparation;

import java.util.ArrayList;

import android.graphics.Point;
/**
 * RasterRegion prilagodjen izvrsavanju na androidu
 * */
public class RasterRegion implements Comparable<RasterRegion> {
	public String Tag = null;
	public int regId = 0;
	public ArrayList<Point> points = new ArrayList<Point>();

	public Boolean momentiOdredjeni = false;
	public int n = 0; // ukupan broj tacaka

	public double c20 = 0;
	public double c11 = 0;
	public double c02 = 0;

	public int minX = Integer.MAX_VALUE;
	public int maxX = 0;
	public int minY = Integer.MAX_VALUE;
	public int maxY = 0;

	// osnovne statisticke osobine regiona
	public double xM = 0;
	public double yM = 0;
	public double theta = 0;
	public double eccentricity = 0;
	public double majorAxisLength = 0;
	public double minorAxisLength = 0;

	public void odrediMomente() {
		xM = 0;
		yM = 0;
		theta = 0;
		eccentricity = 0;
		majorAxisLength = 0;
		minorAxisLength = 0;
		n = points.size();
		for (Point pp : points) {
			xM += pp.x;
			yM += pp.y;

			// odredi "minimum bounding box" regiona
			if (pp.x < minX)
				minX = (int) pp.x;
			if (pp.x > maxX)
				maxX = (int) pp.x;
			if (pp.y < minY)
				minY = (int) pp.y;
			if (pp.y > maxY)
				maxY = (int) pp.y;
		}
		xM = xM / n;
		yM = yM / n;
		for (Point pp : points) {
			c20 += (pp.x - xM) * (pp.x - xM);
			c11 += (pp.x - xM) * (pp.y - yM);
			c02 += (pp.y - yM) * (pp.y - yM);
		}
		// sad imamo vrednosti covariance matrix
		// c20 c11
		// c11 c02
		// odrediti karakteristicne vrednosti
		double a = 1;
		double b = -(c20 + c02);
		double c = c20 * c02 - c11 * c11;
		double D = b * b - 4 * c;
		double alfa1 = 0;
		double alfa2 = 0;
		if (D > 0) {
			D = Math.sqrt(D);
			alfa1 = (-b + D) / 2 * a;
			alfa2 = (-b - D) / 2 * a;
			double temp1 = Math.max(alfa1, alfa2);
			double temp2 = Math.min(alfa1, alfa2);
			alfa1 = temp1;
			alfa2 = temp2;
			if (alfa1 != 0)
				eccentricity = alfa2 / alfa1;
			majorAxisLength = alfa1;
			minorAxisLength = alfa2;
		}
		theta = 0.5 * Math.atan2(2 * c11, (c20 - c02));
	}

	public short[][] odrediSliku() {
		if (!momentiOdredjeni)
			odrediMomente();

		int height = maxY - minY + 1;
		int width = maxX - minX + 1;

		short[][] retVal = new short[height][width];
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				retVal[i][j] = (short) 255;
			}
		}

		for (Point p : points) {
			retVal[(int) (p.y - minY)][(int) (p.x - minX)] = 0;
		}

		return retVal;
	}

	public short[][] odrediNSliku() {
		if (!momentiOdredjeni)
			odrediMomente();

		ArrayList<Point> nPoints = new ArrayList<Point>();

		double ugao = Math.PI / 2 - Math.abs(theta);
		for (Point p : points) {
			double nX = Math.cos(ugao) * (p.x - xM) - Math.sin(ugao) * (p.y - yM) + xM;
			double nY = Math.sin(ugao) * (p.x - xM) + Math.cos(ugao) * (p.y - yM) + yM;

			// nPoints.add(new Point.Double(nX, nY));
			nPoints.add(new Point((int) nX, (int) nY));
		}

		RasterRegion nRegion = new RasterRegion();
		nRegion.points = nPoints;

		return nRegion.odrediSliku();
	}

	public int compareTo(RasterRegion o) {
		return Integer.valueOf(this.minX).compareTo(Integer.valueOf(o.minX));
	}

	public static ArrayList<RasterRegion> regionLabeling(short[][] slika, int step) {
		ArrayList<RasterRegion> regions = new ArrayList<RasterRegion>();
		int h = slika.length;
		int w = slika[0].length;

		// Kreiranje maske za spajanje regiona. 1 je default, 6 je minimum za onu sliku sa wikipedie.
		ArrayList<int[]> masks = createMask(step);

		int[] ii = masks.get(0);
		int[] jj = masks.get(1);
		// int[] ii = { 0, 1, 0, -1 };
		// int[] jj = { 1, 0, -1, 0 };
		int n = ii.length;
		short regNum = 0;
		for (int y = step; y < h - step; y++) {
			for (int x = step; x < w - step; x++) {
				if (slika[y][x] == 0) {
					regNum++;
					short rr = (short) (regNum * 50);
					if (rr == 0)
						rr = 1;
					slika[y][x] = (rr > 255) ? (short) 255 : rr;
					ArrayList<Point> front = new ArrayList<Point>();
					Point pt = new Point(x, y);
					RasterRegion region = new RasterRegion();
					region.regId = regNum;
					region.points.add(pt);
					regions.add(region);
					front.add(pt);
					while (front.size() > 0) {
						Point p = front.get(0);
						front.remove(0);
						for (int t = 0; t < n; t++) {
							Point point = new Point(p.x + jj[t], p.y + ii[t]);
							if (point.x > -step && point.x < w - (step - 1) && point.y > -step && point.y < h - (step - 1)) {
								short pp = slika[(int) point.y][(int) point.x];
								if (pp == 0) {
									slika[(int) point.y][(int) point.x] = slika[y][x];
									region.points.add(point);
									front.add(point);
								}
							}
						}
					}
				}
			}
		}
		return regions;
	}

	private static ArrayList<int[]> createMask(int size) {
		ArrayList<int[]> retVal = new ArrayList<int[]>();

		int n = (2 * size + 1);

		int[] ii = new int[n * (size * 2 + 1) - 1];
		int[] jj = new int[n * (size * 2 + 1) - 1];
		int brojac = 0;

		for (int i = -size; i <= size; i++) {
			for (int j = 0; j < n; j++) {
				if (i == 0 && j == 0)
					continue;

				ii[brojac] = i;
				jj[brojac] = -size + j;

				brojac++;
			}
		}
		retVal.add(ii);
		retVal.add(jj);
		return retVal;
	}

}